package com.chuangjiangx.member.manager.common.excel;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.Date;
import java.util.Iterator;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 导出excel的对象类
 */
public class ExcelExport {

    /**
     * 默认sheet页的每页最大数据量
     **/
    private static final int SHEET_DEFAULT_DATA_SIZE = 10000;
    /**
     * 导出excel的线程池
     */
    private static final ExecutorService EXPORT_EXCEL_THREAD_POOL = Executors.newFixedThreadPool(100);

    private CellStyle dateCellStyle;

    private Workbook workbook;
    private final ExcelHeader excelHeader;
    private final ExcelData excelData;

    /**
     * 实例化一个默认的excel导出类
     *
     * @param excelHeader excel表头信息
     * @param excelData   excel数据信息
     */
    public ExcelExport(final ExcelHeader excelHeader, final ExcelData excelData) {
        this(new XSSFWorkbook(), excelHeader, excelData);
    }

    /**
     * 指定Workbook对象，实例化excel导出类
     *
     * @param workbook    自定义的workbook对象
     * @param excelHeader excel表头信息
     * @param excelData   excel数据信息
     */
    public ExcelExport(Workbook workbook, final ExcelHeader excelHeader, final ExcelData excelData) {
        this.workbook = workbook;
        this.excelHeader = excelHeader;
        this.excelData = excelData;

        dateCellStyle = workbook.createCellStyle();
        CreationHelper creationHelper = workbook.getCreationHelper();
        dateCellStyle.setDataFormat(creationHelper.createDataFormat().getFormat("yyyy-MM-dd  hh:mm:ss"));
    }

    public void buildExcel() throws InterruptedException {
        int sheetNum = excelData.size() / SHEET_DEFAULT_DATA_SIZE;
        sheetNum += excelData.size() % SHEET_DEFAULT_DATA_SIZE > 0 ? 1 : 0;
        CountDownLatch latch = new CountDownLatch(sheetNum);
        for (int i = 0; i < sheetNum; i++) {
            final Iterable<Object> data = excelData.subData(i * SHEET_DEFAULT_DATA_SIZE, SHEET_DEFAULT_DATA_SIZE);
            Sheet sheet = workbook.createSheet(String.format("第%d页", i + 1));
            EXPORT_EXCEL_THREAD_POOL.submit(() -> buildSheet(sheet, data, latch));
        }
        latch.await();
    }

    private void buildSheet(Sheet sheet, Iterable<Object> dataColl, CountDownLatch latch) {
        try {
            Iterator<Object> dataIterator = dataColl.iterator();
            int rowNum = 0;

            Row row = sheet.createRow(rowNum++);
            buildHeader(row);

            while (dataIterator.hasNext()) {
                row = sheet.createRow(rowNum++);

                Object data = dataIterator.next();
                ExcelHeader.Header[] headers = excelHeader.getHeaders();
                for (int i = 0; i < headers.length; i++) {
                    String fieldName = headers[i].getFieldName();
                    Field field = ReflectionUtils.findField(data.getClass(), fieldName);
                    if (field != null) {
                        field.setAccessible(true);
                        Object value = ReflectionUtils.getField(field, data);
                        Cell cell = row.createCell(i);
                        setCellValue(cell, value);
                    }
                }
            }
        } finally {
            latch.countDown();
        }
    }

    /**
     * 构造表头
     *
     * @param row 表头行对象
     */
    private void buildHeader(Row row) {
        int columnNum = 0;
        for (ExcelHeader.Header header : excelHeader.getHeaders()) {
            Cell cell = row.createCell(columnNum++);
            setCellValue(cell, header.getName());
        }
    }

    private void setCellValue(Cell cell, Object value) {
        if (value == null) {
            return;
        }
        if (value instanceof String) {
            cell.setCellType(CellType.STRING);
            cell.setCellValue((String) value);
        } else if (value instanceof Date) {
            cell.setCellStyle(dateCellStyle);
            cell.setCellValue((Date) value);
        } else if (value instanceof Number) {
            cell.setCellType(CellType.NUMERIC);
            cell.setCellValue(((Number) value).doubleValue());
        } else {
            cell.setCellType(CellType.STRING);
            cell.setCellValue(value.toString());
        }
    }

    public Workbook getWorkbook() {
        return workbook;
    }

    public ExcelHeader getExcelHeader() {
        return excelHeader;
    }

    public ExcelData getExcelData() {
        return excelData;
    }
}
